using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data.Odbc;
using System.Data.OleDb;
using BesAsm.Framework.MsAccessUtilities;
using BesAsm.Framework.ModelResults;
using System.Globalization;

namespace BesAsm.Framework.XpSwmmUtilities
{
  /// <summary>
  /// Extracts results from a version 11.2-12.0 (Retail 9.0-10.6, Engine 8.87-10.60)
  /// XP-SWMM output file. Will extract Table E09, Table E10, Table E18, Table E19 and Table E20. Output
  /// can be obtained through the GetTableXX() functions, which return strongly-typed 
  /// DataSets included in this assembly, or by calling WriteToAccessDatabase() which
  /// places the output tables in the provided database.
  /// </summary>
  public class XpSwmmResultsProvider : IModelResultsProvider
  {
    private string _outputFile;
    private StreamReader _outputReader;

    private DateTime _beginDateTime;

    private IModelResultsHost _host;
    private const string APPLICATION_NAME = "XP SWMM";
    private const string VENDOR = "XP Software";
    private const string FILE_FILTER = "SWMM output file|*.out";
    private const string SUPPORTED_VERSIONS = "10.0";

    public string ApplicationName
    {
      get
      {
        return APPLICATION_NAME;
      }
    }
    public string Vendor
    {
      get
      {
        return VENDOR;
      }

    }
    public string FileExtensionFilter
    {
      get
      {
        return FILE_FILTER;
      }

    }
    public string SupportedVersions
    {
      get
      {
        return SUPPORTED_VERSIONS;
      }

    }

    public IModelResultsHost ModelResultsHost
    {
      get
      {
        return _host;
      }
      set
      {
        _host = value;
      }
    }

    ModelResultsSet _results;
   
    public ModelResultsSet LoadResults(string file)
    {
      _outputReader = null;
      _results = new ModelResultsSet();

      try
      {
        _outputReader = new StreamReader(file);

        ValidateOutputFile();

        this._beginDateTime = ExtractStartDateTime();

        ParseTableE09();
        ParseTableE10();
        ParseTableE18();
        ParseTableE19();
        ParseTableE20();

        _results.ModelEngine = APPLICATION_NAME;
        _results.SimulationDateTime = System.IO.File.GetLastWriteTime(file);
        _results.Name = System.IO.Path.GetFileNameWithoutExtension(file);        
      }
      catch (Exception ex)
      {
        return null;
      }
      finally
      {
        if (_outputReader != null)
          _outputReader.Close();
      }
      
      return _results;
    }    

    /// <summary>
    /// Creates a new XPSWMMResults processor.
    /// </summary>    
    public XpSwmmResultsProvider()
    {
    }

    private void ValidateOutputFile()
    {
      string currentLine;
      string[] tokens;
      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new Exception("Output file is not valid. Could not determine file version.");
        }
      } while (!currentLine.Contains("Data File Version"));

      tokens = currentLine.Split(new char[] { ' ', '|' }, StringSplitOptions.RemoveEmptyEntries);
      if (Convert.ToDouble(tokens[4]) < 11.2 || Convert.ToDouble(tokens[4]) > 12.0)
      {
        throw new Exception("Output file was version " + tokens[4] + ". Expected 11.2-11.9.");
      }
      return;
    }

    private DateTime ExtractStartDateTime()
    {
      string currentLine;
      string[] tokens;
      int beginYear, beginMonth, beginDay, beginHour, beginMinute, beginSecond;

      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new Exception("Could not find output Start Time.");
        }
      } while (!currentLine.Contains("Time Control from Hydraulics Job Control"));

      currentLine = _outputReader.ReadLine();
      tokens = currentLine.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
      beginYear = Convert.ToInt32(tokens[1]);
      beginYear += beginYear < 100 ? 1900 : 0;
      beginMonth = Convert.ToInt32(tokens[3]);

      currentLine = _outputReader.ReadLine();
      tokens = currentLine.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
      beginDay = Convert.ToInt32(tokens[1]);
      beginHour = Convert.ToInt32(tokens[3]);

      currentLine = _outputReader.ReadLine();
      tokens = currentLine.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
      beginMinute = Convert.ToInt32(tokens[1]);
      beginSecond = Convert.ToInt32(tokens[3]);

      _beginDateTime = new DateTime(beginYear, beginMonth, beginDay, beginHour, beginMinute, beginSecond);

      return _beginDateTime;
    }

    /// <summary>
    /// Gets the output file associated with this instance of XPSWMMResults.
    /// </summary>
    public string OutputFile
    {
      get { return this._outputFile; }
    }

    /// <summary>
    /// Gets the date and time that the XP-SWMM output starts at.
    /// </summary>
    public DateTime BeginDateTime
    {
      get { return this._beginDateTime; }
    }

    private int ParseTableE09()
    {
      NodeResult nr;

      string currentLine = "";
      string[] tokens;
      string token = "";

      string nodeName;

      double grElev;
      double maxCrown;
      double maxJElev;
      double hour;
      double minute;
      System.DateTime timeOfMax;
      double surcharge;
      double freeboard;
      double maxArea;

      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new IOException("Table E09 not found within " + this._outputFile);
        }
      } while (!currentLine.Contains("Table E9 - JUNCTION SUMMARY STATISTICS"));

      try
      {
        for (int i = 0; i < 10; i++)
        {
          currentLine = _outputReader.ReadLine();
        }

        while (currentLine != "")
        {
          nr = new NodeResult();

          tokens = currentLine.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

          nodeName = tokens[0];

          token = tokens[1];
          grElev = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[2];
          maxCrown = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[3];
          maxJElev = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          hour = Convert.ToDouble(tokens[4]);
          minute = Convert.ToDouble(tokens[5]);
          timeOfMax = this._beginDateTime.AddHours(hour + minute / 60);

          token = tokens[6];
          surcharge = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[7];
          freeboard = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[8];
          maxArea = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          nr.NodeName = nodeName;
          nr.MaxWaterElev = maxJElev;
          nr.MaxSurcharge = surcharge;
          nr.MinFreeboard = freeboard;

          _results.NodeResults.Add(nodeName, nr);

          currentLine = _outputReader.ReadLine();
        }
      }
      catch (Exception ex)
      {
        throw new Exception("Error parsing Table E09: " + ex.Message);
      }

      return _results.NodeResults.Count;
    }

    private int ParseTableE10()
    {
      LinkResult lr;

      string currentLine = "";
      string[] tokens;
      string token;

      string condName;

      double designQ;
      double designV;
      double maxD;
      double maxQ;
      double hour;
      double minute;
      System.DateTime timeMaxQ;
      double maxV;
      System.DateTime timeMaxV;
      double qQRatio;
      double maxUsElev;
      double maxDsElev;

      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new Exception("Table E10 not found within " + this._outputFile);
        }
      } while (!currentLine.Contains("Table E10 - CONDUIT SUMMARY STATISTICS"));

      try
      {
        for (int i = 0; i < 14; i++)
        {
          currentLine = _outputReader.ReadLine();
        }

        while (currentLine != "")
        {
          lr = new LinkResult();

          tokens = currentLine.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

          switch (tokens.Length)
          {
            case 7: // Orifice, Weir                           
              break;
            case 8: //Free #
              break;
            case 9: //Free #
              break;
            case 15:
              condName = tokens[0];
              token = tokens[1];
              designQ = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              token = tokens[2];
              designV = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              token = tokens[3];
              maxD = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              token = tokens[4];
              maxQ = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              hour = Convert.ToInt32(tokens[5]);
              minute = Convert.ToInt32(tokens[6]);
              timeMaxQ = this._beginDateTime.AddHours(hour + minute / 60);

              token = tokens[7];
              maxV = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              hour = Convert.ToInt32(tokens[8]);
              minute = Convert.ToInt32(tokens[9]);
              timeMaxV = this._beginDateTime.AddHours(hour + minute / 60);

              token = tokens[10];
              qQRatio = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              token = tokens[11];
              maxUsElev = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              token = tokens[12];
              maxDsElev = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

              lr.LinkName = condName;
              lr.MaxQqRatio = qQRatio;
              lr.MaxFlow = maxQ;
              lr.MaxVelocity = maxV;
              lr.MaxUsWaterElev = maxUsElev;
              lr.MaxDsWaterElev = maxDsElev;

              _results.LinkResults.Add(condName, lr);

              break;
            default:
              throw new Exception();
          }
         
          
          currentLine = _outputReader.ReadLine();
        }
      }
      catch (Exception ex)
      {
        throw new Exception("Error parsing Table E10: " + ex.Message);
      }

      return 0;// tableE10.Count;
    }

    private int ParseTableE18()
    {
      string currentLine = "";
      string[] tokens;
      string nodeName;
      string stoVolString;
      double storageVolumeCuFt;

      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new Exception("Table E18 not found within " + this._outputFile);
        }

      } while (!currentLine.Contains("Table E18 - Junction Continuity Error"));

      try
      {
        for (int i = 0; i < 18; i++)
        {
          currentLine = _outputReader.ReadLine();
        }

        while (!currentLine.Contains("The total continuity error was"))
        {
          tokens = currentLine.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
          nodeName = tokens[0];
          stoVolString = tokens[4];
          storageVolumeCuFt = Double.Parse(stoVolString, NumberStyles.Any, CultureInfo.InvariantCulture);
          //storageVolumeCuFt = Convert.ToDouble(tokens[4]);

          //tableE18.AddTableE18Row(nodeName, storageVolumeCuFt);
          currentLine = _outputReader.ReadLine();
        }
      }

      catch (Exception ex)
      {
        throw new Exception("Error parsing Table E18: " + ex.Message);
      }

      return 0;// tableE18.Count;
    }

    private int ParseTableE19()
    {
      string currentLine = "";
      string[] tokens;
      string token;

      string nodeName;

      double interfaceInflowCuFt;
      double infiltrationVolumeCuFt;

      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new Exception("Table E19 not found within " + this._outputFile);
        }

      } while (!currentLine.Contains("Table E19 - Junction"));

      try
      {
        for (int i = 0; i < 9; i++)
        {
          currentLine = _outputReader.ReadLine();
        }

        while (currentLine != "")
        {
          tokens = currentLine.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
          nodeName = tokens[0];

          token = tokens[3];
          interfaceInflowCuFt = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[7];
          infiltrationVolumeCuFt = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          currentLine = _outputReader.ReadLine();
        }
      }

      catch (Exception ex)
      {
        throw new Exception("Error Parsing Table E19: " + ex.Message);
      }
      finally
      {

      }
      return 0;// tableE19.Count;

    }

    private int ParseTableE20()
    {
      NodeResult nr;

      string currentLine = "";
      string[] tokens;
      string token = "";

      string nodeName;      
      double surchargeTime;      
      double floodedTime;      
      double floodVol;      
      double maxStoredVol;      
      double pondingVol;

      do
      {
        currentLine = _outputReader.ReadLine();
        if (_outputReader.EndOfStream)
        {
          throw new Exception("Table E20 not found within " + this._outputFile);
        }
      } while (!currentLine.Contains("Table E20 - Junction Flooding and Volume Listing"));

      try
      {
        for (int i = 0; i < 18; i++)
        {
          currentLine = _outputReader.ReadLine();
        }

        while (currentLine != "")
        {
          tokens = currentLine.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

          nodeName = tokens[0];

          if (!_results.NodeResults.ContainsKey(nodeName))
            continue;
          nr = _results.NodeResults[nodeName];

          token = tokens[1];
          surchargeTime = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[2];
          floodedTime = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[3];
          floodVol = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[4];
          maxStoredVol = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          token = tokens[5];
          pondingVol = Double.Parse(token, NumberStyles.Any, CultureInfo.InvariantCulture);

          nr.SurchargedTime = surchargeTime;
          nr.FloodedTime = floodedTime;          
          
          currentLine = _outputReader.ReadLine();
        }
      }
      catch (Exception ex)
      {
        throw new Exception("Error parsing Table E20: " + ex.Message);
      }

      return 0;// tableE20.Count;
    }
  }
}
